package com.apestech.api.workflow.lib.element;

import com.apestech.api.workflow.extend.FlowDatabase;
import com.apestech.framework.util.FileUtil;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.api.Task;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;

/**
 * flow执行
 *
 * @author hup
 * @create 2018-07-04-10:20
 */
public class FlowExecute {
    private WorkProcessEngine engine;
    private String bpmnDir;

    public FlowExecute(WorkProcessEngine engine){
        this.engine=engine;
    }

    public void setBpmnDir(String bpmnDir) {
        this.bpmnDir = bpmnDir;
    }

    public void refreshDeployment() throws Exception{
        ArrayList<String> flist=FileUtil.getFieldList(bpmnDir);
        int len=flist.size();
        if(len>0){
            DeploymentBuilder builder = engine.getRepositoryService()//获取流程定义和部署对象相关的Service
                    .createDeployment();//创建部署对象
            for(int i=0; i<len; i++){
                String file=flist.get(i);
                if(file.toLowerCase().indexOf(".bpmn")>=0){
                    FileInputStream bpmnfileInputStream = new FileInputStream(file);;
                    builder.addInputStream(file, bpmnfileInputStream);
                }
            }
            Deployment deployment=builder.deploy();
            System.out.println("部署ID："+deployment.getId());
            System.out.println("部署时间："+deployment.getDeploymentTime());
        }
    }

    /**
     * 获取所有流程定义
     * @return
     * @throws Exception
     */
    public ProcessDefinition getProcessDefinition(String processDefinitionId)throws Exception{
        ProcessDefinition processDefinition = engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        return processDefinition;
    }

    /**
     * 获取流程定义资源
     * @param processDefinitionId
     * @param resourceType
     * @return
     * @throws Exception
     */
    public InputStream getProcessResource(String processDefinitionId, String resourceType)throws Exception{
        ProcessDefinition processDefinition = getProcessDefinition(processDefinitionId);
        String resourceName = "";
        if(resourceType.equals("image")){
            resourceName = processDefinition.getDiagramResourceName();
        }else if(resourceType.equals("xml")){
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = engine.getRepositoryService().getResourceAsStream(processDefinition.getDeploymentId(),resourceName);
        return resourceAsStream;
    }

    /**
     * 获取流程实例的流程状态图
     * @param processInstanceId
     * @return
     * @throws Exception
     */
    public InputStream getProcessInstancePng(String processInstanceId)throws Exception{
        FlowBpmn bpmn= new FlowBpmn(engine, processInstanceId);
        return bpmn.getProcessInstancePng();
    }

    /**
     * 不和业务数据关联
     * @param processDefinitionKey
     * @param variables
     * @return
     * @throws Exception
     */
    public ProcessInstance startProcess(String processDefinitionKey, Map<String, Object> variables)throws Exception{
        //设置启动流程人员的ID，引擎会自动把用户ID保存到initiator中
        String userId = (String)variables.get("userId");
        engine.getIdentityService().setAuthenticatedUserId(userId);
        variables.remove("userId");
        ProcessInstance processInstance = engine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey,variables);
        System.out.println(userId+"发起新的流程:"+processInstance.getName()+"-"+processInstance.getProcessInstanceId()+" \n");
        engine.getIdentityService().setAuthenticatedUserId(null);
        return processInstance;
    }
    /**
     * 和其它系统业务数据关联
     * @param processDefinitionKey
     * @param businessKey //业务数据的主键值，以后可通过该值查询到对应的流程实例ID
     * @param variables
     * @return
     * @throws Exception
     */
    public ProcessInstance startProcess(String processDefinitionKey,String businessKey,Map<String, Object> variables)throws Exception{
        //设置启动流程人员的ID，引擎会自动把用户ID保存到initiator中
        String userId = (String)variables.get("userId");
        engine.getIdentityService().setAuthenticatedUserId(userId);
        variables.remove("userId");
        variables.put("businessKey", businessKey);
        ProcessInstance processInstance = engine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey,businessKey,variables);
        System.out.println(userId+"发起新的流程:"+processInstance.getName()+",businessKey="+processInstance.getBusinessKey()+" \n");
        engine.getIdentityService().setAuthenticatedUserId(null);
        return processInstance;
    }
    public boolean isBusinessKeyExisted(String businessKey)throws Exception{
        boolean businessKeyExisted = false;
        List<Task> task = engine.getTaskService().createTaskQuery().processInstanceBusinessKey(businessKey).list();
        if(task.size()>=1){
            businessKeyExisted = true;
        }
        return businessKeyExisted;
    }
    /**
     * 分配当前流程任务给候选用户组；任务未真正分配。
     * @param taskId
     * @param taskAssignedUserGroupId
     * @throws Exception
     */
    public void assignTaskToGroup(String taskId,String taskAssignedUserGroupId)throws Exception{
        if(taskAssignedUserGroupId==null){
            throw new Exception("组不允许为空！");
        }
        engine.getTaskService().addCandidateGroup(taskId,taskAssignedUserGroupId);
    }

    /**
     * 分配当前流程任务给候选用户组；任务未真正分配。
     * @param taskId
     * @param grouplist
     * @throws Exception
     */
    public void assignTaskToGroup(String taskId,ArrayList<String> grouplist)throws Exception{
        if(grouplist==null){
            throw new Exception("组列表不允许为空！");
        }
        int len=grouplist.size();
        for(int i=0; i<len; i++){
            assignTaskToGroup(taskId,grouplist.get(i));
        }
    }

    /**
     * 分配当前流程给候选用户，任务为真正分配，需签收
     * @param taskId
     * @param userId
     * @throws Exception
     */
    public void assignTaskToUser(String taskId,String userId)throws Exception{
        if(userId==null){
            throw new Exception("人员不允许为空！");
        }
        engine.getTaskService().addCandidateUser(taskId,userId);
    }

    /**
     * 分配当前流程给候选用户，任务为真正分配，需签收
     * @param taskId
     * @param userlist
     * @throws Exception
     */
    public void assignTaskToUser(String taskId,ArrayList<String> userlist)throws Exception{
        if(userlist==null){
            throw new Exception("人员列表不允许为空！");
        }
        FlowDatabase db=new FlowDatabase(engine);
        int len=userlist.size();
        for(int i=0; i<len; i++){
            String userId=db.getCommission(userlist.get(i), null).getId();
            if(userId==null){
                userId=userlist.get(i);
            }
            assignTaskToUser(taskId,userId);
        }
    }

    /**
     * 具体人签收任务
     * @param taskId
     * @param claminTaskUserId
     * @throws Exception
     */
    public void claimTask(String taskId,String claminTaskUserId)throws Exception{
        engine.getTaskService().claim(taskId,claminTaskUserId);
    }

    /**
     * 具体人取消签收任务
     * @param taskId
     * @throws Exception
     */
    public void unClaimTask(String taskId)throws Exception{
        engine.getTaskService().unclaim(taskId);
    }

    /**
     * 转签
     * @param taskId
     * @param claminTaskUserId
     * @throws Exception
     */
    public void changeSignTask(String taskId,String claminTaskUserId)throws Exception{
        unClaimTask(taskId);
        claimTask(taskId,claminTaskUserId);
    }

    /**
     * 处理任务
     * @param taskId
     * @param variables
     * @throws Exception
     */
    public void processTask(String taskId,Map<String, Object> variables)throws Exception{
        engine.getTaskService().complete(taskId,variables);
    }

    /**
     * 挂起运行流程
     * @param processInstanceId
     */
    public void suspendProcess(String processInstanceId){
        engine.getRuntimeService().suspendProcessInstanceById(processInstanceId);
    }

    /**
     * 唤醒运行流程
     * @param processInstanceId
     */
    public void activateProcess(String processInstanceId){
        engine.getRuntimeService().activateProcessInstanceById(processInstanceId);
    }

    /**
     * 挂起定义流程
     * @param processDefinitionId
     */
    public void suspendProcessDefine(String processDefinitionId){
        engine.getRepositoryService().suspendProcessDefinitionById(processDefinitionId);
    }

    /**
     * 挂起定义流程
     * @param processDefinitionId
     * @param suspendProcessInstances 是否级联挂起该流程定义的流程实例
     * @param suspensionDate 挂起这个流程定义的时间，null为立即挂起
     */
    public void suspendProcessDefine(String processDefinitionId,boolean suspendProcessInstances,Date suspensionDate){
        engine.getRepositoryService().suspendProcessDefinitionById(processDefinitionId, suspendProcessInstances, suspensionDate);
    }

    /**
     * 唤醒定义流程
     * @param processDefinitionId
     */
    public void activateProcessDefine(String processDefinitionId){
        engine.getRepositoryService().activateProcessDefinitionById(processDefinitionId);
    }

    /**
     * 唤醒定义流程
     * @param processDefinitionId
     * @param activateProcessInstances 是否级联唤醒该流程定义的流程实例
     * @param activationDate 唤醒这个流程定义的时间，null为立即唤醒
     */
    public void activateProcessDefine(String processDefinitionId, boolean activateProcessInstances, Date activationDate){
        engine.getRepositoryService().activateProcessDefinitionById(processDefinitionId, activateProcessInstances, activationDate);
    }

    /**
     * 终止整个流程
     * @param processInstanceId 流程ID
     * @param deleteReason 删除原因
     */
    public void stopProcess(String processInstanceId, String deleteReason){
        engine.getRuntimeService().deleteProcessInstance(processInstanceId,deleteReason);
    }

    /**
     * 清除任务结点里所有的候选人
     * @param taskId
     */
    public void deleteTaskAllUser(String taskId){
        List<IdentityLink> identityLinkList = engine.getTaskService().getIdentityLinksForTask(taskId);
        for (IdentityLink id : identityLinkList){
            String userId=id.getUserId();
            String groupId=id.getGroupId();
            if(userId!=null){
                engine.getTaskService().deleteCandidateUser(taskId, userId);
            } else if(groupId!=null){
                engine.getTaskService().deleteCandidateGroup(taskId, groupId);
            }
        }
    }
}
